package com.fanghuaiming.mtat.sku.service.impl;

import com.fanghuaiming.mtat.sku.common.CustomizeTransactionManager;
import com.fanghuaiming.mtat.sku.dao.mapper.MtatSkuMapper;
import com.fanghuaiming.mtat.sku.dao.model.MtatSku;
import com.fanghuaiming.mtat.sku.service.MessageListOrderService;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

/****
 * @description: sku服务接口实现类
 * @version:1.0.0
 * @author fanghuaiming
 * @data Created in 2020/11/27 下午1:59
 *
 */
@Service
@Slf4j
public class MessageListOrderServiceImpl implements MessageListOrderService {

    /**
     * SKU的dao
     */
    @Autowired
    private MtatSkuMapper mtatSkuMapper;

    /**
     * 自定义事务管理器
     */
    @Autowired
    private CustomizeTransactionManager customizeTransactionManager;

    /**
     * 子线程是否能进行提交
     */
    private static volatile boolean COMMIT_OK = true;

    /**
     * 自定义多线程
     */
    private ExecutorService executorService = new ThreadPoolExecutor(
            5,
            10,
            100,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2000),(runnable)-> {
                Thread thread = new Thread(runnable);
                thread.setName("batch-child-thread-handle");
                return thread;
    });


    /**
     * @Description: 一次性提交多少数据
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/12/4 下午7:47
     * @version:1.0.0
     */
    @SuppressWarnings("all")
    @Override
    @Transactional(transactionManager = "mtatSkuTransactionManager")
    public void insertSkuByNumber(Integer number) {
        List<MtatSku> mtatSkuList = new ArrayList<>();
        for (int i = 0; i < number; i++) {
            MtatSku mtatSku = new MtatSku();
            mtatSku.setNum(1);
            mtatSku.setPrice(100d);
            mtatSku.setTitle("fanghuaiming" + i);
            mtatSkuList.add(mtatSku);
        }
        mtatSkuMapper.batchInsertSku(mtatSkuList);
    }

    /**
     * @Description: 单条插入数据库
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/12/4 下午9:55
     * @version:1.0.0
     */
    @Transactional(transactionManager = "mtatSkuTransactionManager")
    @SuppressWarnings("all")
    @Override
    public void insertSku(Integer number) {
        for (int i = 0; i < number; i++) {
            MtatSku mtatSku = new MtatSku();
            mtatSku.setNum(1);
            mtatSku.setPrice(100d);
            mtatSku.setTitle("fanghuaiming" + i);
            mtatSkuMapper.insertSku(mtatSku);
        }
    }

    /**
     * @Description: 自定义事务管理器
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/12/7 上午10:17
     * @version:1.0.0
     */
    @Resource(name = "mtatSkuTransactionManager")
    private DataSourceTransactionManager dataSourceTransactionManager;

    /**
     * @Description: 事务definition
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/12/7 上午10:17
     * @version:1.0.0
     */
    @Autowired
    TransactionDefinition transactionDefinition;

    /**
     * @Description: 多线程批量插入
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/12/4 下午10:13
     * @version:1.0.0
     */
    @SuppressWarnings("all")
    @Override
    public void batchThreadHandle(Boolean makeException) {
        int threadCount = 5;
        //主线程等待子线程执行完成 倒计时器
        CountDownLatch childCountDownLatch = new CountDownLatch(threadCount);
        //主线程收到子线程的最终结果
        ArrayList<Boolean> chaildThreadResponse = new ArrayList<>();
        //子线程在各自线程上等待主线程倒计时器倒计时
        CountDownLatch batchThreadCountDownLatch = new CountDownLatch(1);
        for (int i = 0; i < threadCount ; i++){
            //标记线程是第几个
            int threadth = i;
            executorService.execute(() ->{
                //手动开启事务
                //TransactionStatus transactionStatus = customizeTransactionManager.begin();
                TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
                try {
                    if(threadth == 4 && makeException){
                        throw new Exception("开启某个线程模拟异常");
                    }
                    //提交10万条数据
                    mtatSkuMapper.batchInsertSku(buildSkuList());
                    //子线程提交10万条数据正常
                    chaildThreadResponse.add(Boolean.TRUE);
                    //子线程倒计时器减一,所有子线程都执行到这里倒计时器里就变为0,这样在await地方就可以继续执行了,对应第203行的await
                    childCountDownLatch.countDown();
                    log.info("子线程{}执行完成,等待其他线程执行结束,判断是否需要回滚", Thread.currentThread().getName()+ threadth);
                    //所有子线程执行到这一步进行等待主线程检查所有子线程是否执行都ok(无论成功还是失败),对应第216行的countDown
                    batchThreadCountDownLatch.await();
                    if(COMMIT_OK){

                        //手动提交各子线程自己的事务
                        //customizeTransactionManager.commit(transactionStatus);
                        //platformTransactionManager.commit(TransactionAspectSupport.currentTransactionStatus());
                        dataSourceTransactionManager.commit(transactionStatus);
                        log.info("所有子线程执行完成,线程{}事务提交", Thread.currentThread().getName()+ threadth);
                    }else {
                        //手动回滚各子线程自己的事务
                        //customizeTransactionManager.rollBack();
                        //platformTransactionManager.rollback(TransactionAspectSupport.currentTransactionStatus());
                        dataSourceTransactionManager.rollback(transactionStatus);
                        log.info("有子线程执行失败,线程{}事务进行回滚", Thread.currentThread().getName()+ threadth);
                    }
                } catch (Exception e) {
                    //如果某个子线程出现异常
                    chaildThreadResponse.add(Boolean.FALSE);
                    //当前子线程 倒计时器减一
                    childCountDownLatch.countDown();
                    //customizeTransactionManager.rollBack();
                    //platformTransactionManager.rollback(TransactionAspectSupport.currentTransactionStatus());
                    dataSourceTransactionManager.rollback(transactionStatus);
                    log.error("子线程{}发生了异常,开始进行事务回滚,异常原因:{}", Thread.currentThread().getName()+ threadth,e.getCause());
                }
            });
        }
        try {
            //主线程等待所有子线程提交数据执行完成,只有childCountDownLatch = 0 才会执行后面的代码,对应171行的countDown
            childCountDownLatch.await();
            for (Boolean response : chaildThreadResponse){
                //如果有一个子线程的结果为false 代表有一个子线程提交是失败的
                if(!response){
                    log.info("有某个子线程{}:COMMIT_OK修改成了false,代表该批次10万条数据失败", Thread.currentThread().getName());
                    COMMIT_OK = false;
                    break;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            //主线程获取结果成功,让子线程开始根据主线程的结果进行提交或者回滚,对应172行的await
            batchThreadCountDownLatch.countDown();
        }
    }

    /**
     * @Description: 创建list对象
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/12/4 下午10:22
     * @version:1.0.0
     */
    private List<MtatSku> buildSkuList() {
        List<MtatSku> list = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            MtatSku mtatSku = new MtatSku();
            mtatSku.setNum(1);
            mtatSku.setPrice(100d);
            mtatSku.setTitle("fanghuaiming" + i);
            list.add(mtatSku);
        }
        return list;
    }

}
